﻿using GalaSoft.MvvmLight;

namespace InternetBank.ViewModel
{
    using System.Collections.Generic;
    using System.IO;
    using System.Security.Cryptography;
    using System.Text;
    using System.Windows;
    using System.Linq;

    using GalaSoft.MvvmLight.Command;

    using HelpersAndContracts.Contracts;
    using HelpersAndContracts.Helpers;

    using InternetBank.BankService;
    using InternetBank.Model;
    using InternetBank.View;

    public class MainViewModel : ViewModelBase
    {
        private readonly IPaymentsService paymentsService;

        private string username;

        public RelayCommand AuthenticateCommand { get; set; }
        public RelayCommand<string> CellularCommand { get; set; }

        private bool isUserLogged;

        private string greetingsText;

        private string balance;

        private string cardId;

        private IEnumerable<PaymentModel> payments;

        public bool IsUserLogged
        {
            get
            {
                return isUserLogged;
            }

            set
            {
                if (isUserLogged == value)
                {
                    return;
                }

                isUserLogged = value;
                RaisePropertyChanged("IsUserLogged");
            }
        }

        public string GreetingsText
        {
            get
            {
                return greetingsText;
            }

            set
            {
                if (greetingsText == value)
                {
                    return;
                }

                greetingsText = value;
                RaisePropertyChanged("GreetingsText");
            }
        }

        public string CardId
        {
            get
            {
                return cardId;
            }

            set
            {
                if (cardId == value)
                {
                    return;
                }

                cardId = value;
                RaisePropertyChanged("CardId");
            }
        }

        public string Balance
        {
            get
            {
                return balance;
            }

            set
            {
                if (balance == value)
                {
                    return;
                }

                balance = value;
                RaisePropertyChanged("Balance");
            }
        }

        public IEnumerable<PaymentModel> Payments
        {
            get
            {
                return payments;
            }

            set
            {
                if (payments == value)
                {
                    return;
                }

                payments = value;
                RaisePropertyChanged("Payments");
            }
        }

        private ECDiffieHellmanCng cryptographyAlgorithm = new ECDiffieHellmanCng();

        private Aes aes = new AesCryptoServiceProvider();

        private byte[] clientPublicKey;

        private byte[] clientSecreteKey;

        private byte[] initializationVector;

        public MainViewModel()
        {
            this.paymentsService = new PaymentsServiceClient();
            ApplyProtocolAndExchangeKeys();
            this.AuthenticateCommand = new RelayCommand(this.Authenticate);
            this.CellularCommand = new RelayCommand<string>(this.MobilePayment);
        }

        private void ApplyProtocolAndExchangeKeys()
        {
            cryptographyAlgorithm.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
            cryptographyAlgorithm.HashAlgorithm = CngAlgorithm.Sha256;
            clientPublicKey = cryptographyAlgorithm.PublicKey.ToByteArray();
            var serverPublicKey = this.paymentsService.GetServerPublicKey("name");
            this.clientSecreteKey = cryptographyAlgorithm.DeriveKeyMaterial(CngKey.Import(serverPublicKey, CngKeyBlobFormat.EccPublicBlob));
            this.paymentsService.GenerateServerSecretKey(clientPublicKey, aes.IV);
            initializationVector = this.paymentsService.GetinitializationVector();
        }

        private byte[] EncodeMessage(string secretMessage)
        {
            aes.Key = this.clientSecreteKey;
            using (MemoryStream ciphertext = new MemoryStream())
            using (CryptoStream cs = new CryptoStream(ciphertext, aes.CreateEncryptor(), CryptoStreamMode.Write))
            {
                byte[] plaintextMessage = Encoding.UTF8.GetBytes(secretMessage);
                cs.Write(plaintextMessage, 0, plaintextMessage.Length);
                cs.Close();
                return ciphertext.ToArray();
            }
        }

        private void MobilePayment(string recipient)
        {
            if (this.CheckTransactionPassword())
            {
                MobilePaymentDialogWindow dialog = new MobilePaymentDialogWindow();
                dialog.ShowDialog();

                var payment = dialog.DataContext as PaymentModelBase;
                if (payment == null)
                {
                    return;
                }
                if (!this.paymentsService.ApplyPayment(new PaymentModel { Username = username, Number = payment.Number, Amount = payment.Amount, Recipient = recipient }.MapAndEncode<EncodedPaymentModel>(aes, clientSecreteKey))) 
                {
                    MessageBox.Show("Please check email for MCode");
                    MCodeDialogWindow mCodeDialog = new MCodeDialogWindow();
                    mCodeDialog.ShowDialog();
                    var mCode = mCodeDialog.DataContext as CredentialsViewModel;
                    if (mCode != null)
                    {
                        this.paymentsService.CheckMCodeAndSubmitPayment(new CredentialsModel{Username = username, Password = mCode.Password}.MapAndEncode<EncodedCredentialsModel>(aes,clientSecreteKey));
                    }
                }
                Payments = this.paymentsService.GetUserPayments(this.username).Select(x => x.MapAndDecode<PaymentModel>(clientSecreteKey, initializationVector));
                MessageBox.Show("Операция успешно произведена"); 
            }
            else
            {
                MessageBox.Show("Wrong Password");
            }
        }

        private void Authenticate()
        {
            LoginDialogWindow mCodeDialogWindow = new LoginDialogWindow();
            if (mCodeDialogWindow.ShowDialog() ?? false)
            {
                var res = mCodeDialogWindow.DataContext as CredentialsViewModel;

                if (res != null &&
                    paymentsService.Authenticate(new CredentialsModel { Password = res.Password, Username = res.Username }.MapAndEncode<EncodedCredentialsModel>(aes, this.clientSecreteKey)))
                {
                    IsUserLogged = true;
                    username = res.Username;
                    this.GetBalance();
                    this.GetUserPayments();
                }
            }
        }

        private void GetBalance()
        {
            var balanceModel = paymentsService.GetBalance(username).MapAndDecode<BalanceModel>(clientSecreteKey,initializationVector);

            this.GreetingsText = string.Format("Hello {0}", balanceModel.Username);
            this.Balance = balanceModel.Balance;
            this.CardId = balanceModel.CardId;
        }

        private bool CheckTransactionPassword()
        {
            TransactionPasswordDialogWindow transactionPasswordDialogWindow = new TransactionPasswordDialogWindow();
            if (transactionPasswordDialogWindow.ShowDialog() ?? false)
            {
                var res = transactionPasswordDialogWindow.DataContext as CredentialsViewModel;
                return res != null && paymentsService.CheckTransactionPassword(new CredentialsModel{ Username = username, Password = res.Password}.MapAndEncode<EncodedCredentialsModel>(aes, clientSecreteKey));
            }
            return false;
        }

        private void GetUserPayments()
        {
            Payments = this.paymentsService.GetUserPayments(this.username).Select(x=>x.MapAndDecode<PaymentModel>(clientSecreteKey, initializationVector));
        }
    }
}